/******************************************************/
/* Goblin's Cube - Rubik's cube game for Wii Homebrew */
/* Copyright (C) 2009 Ricardo Bueno Cordeiro          */
/* Licensed under de GNU General Public License v2.0  */
/*   See license_gplv2.txt for detalis                */
/******************************************************/

#include <malloc.h>
#include <string.h>
#include <asndlib.h>

#include "sound.h"
#include "draw.h"

#include "cube1_wav.h"
#include "cube2_wav.h"
#include "cube3_wav.h"
#include "cube4_wav.h"

#define U32_LITTLE_TO_BIG(u) ((((u) & 0x000000ff) << 24) | (((u) & 0x0000ff00) << 8) | (((u) & 0x00ff0000) >> 8) | (((u) & 0xff000000) >> 24))
#define U16_LITTLE_TO_BIG(u) ((((u) & 0xff00) >> 8) | (((u) & 0x00ff) << 8))

static struct sound_t sounds[SOUND_NUM_SOUNDS];

void sound_init() {
	sound_load();
	ASND_Init();
}

void sound_end() {
	ASND_End();
}

void sound_load_samples(struct sound_t *s, const u8 *file, u32 size) {
	s->loaded = 0;

	// Store sound description
	s->fmt = (struct wave_fmt_block_t *)(file + sizeof(struct riff_file_t) + sizeof(struct wave_header_t));

	// Get data address
	struct wave_data_block_t *data = (struct wave_data_block_t *)(((u8 *)s->fmt) + sizeof(struct wave_block_header_t) + U32_LITTLE_TO_BIG(s->fmt->header.size));
	
	// Store data real size
	s->size = U32_LITTLE_TO_BIG(data->header.size);

	// Store data pitch
	s->pitch = U32_LITTLE_TO_BIG(s->fmt->sample_rate);
	
	u16 bits = U16_LITTLE_TO_BIG(s->fmt->significant_bits);
	u16 num_channel = U16_LITTLE_TO_BIG(s->fmt->num_channel);
	
	// Set data format, if valid
	if (bits == 8) {
		switch (num_channel) {
			case 1: s->format = VOICE_MONO_8BIT; break;
			case 2: s->format = VOICE_STEREO_8BIT; break;
			default: return; // this will let the loaded flag unmarked
		}
	} else if (bits == 16) {
		switch (num_channel) {
			case 1: s->format = VOICE_MONO_16BIT; break;
			case 2: s->format = VOICE_STEREO_16BIT; break;
			default: return; // this will let the loaded flag unmarked
		}
	} else
		return; // this will let the loaded flag unmarked

	// Allocate 32 bytes aligned and padded buffer
	u32 padded_size = (s->size & ~0x1f) + 0x20;
	s->aligned_samples = memalign(32, padded_size);
	memset(s->aligned_samples, 0, padded_size);
	
	if (bits == 8) {
		u8 *src = (u8 *)(&(data->samples));
		u8 *dst = (u8 *)s->aligned_samples;
		u8 *end = src + s->size;
		
		while(src < end)
			*dst++ = *src++;

	} else {
		u8 *src = (u8 *)(&(data->samples));
		u8 *dst = (u8 *)s->aligned_samples;
		u8 *end = src + s->size;
		
		while(src < end) {
			*dst++ = *(src + 1);
			*dst++ = *src;
			src += 2;
		}
	}

	s->loaded = 1;
}

void sound_load() {
	sound_load_samples(&sounds[0], cube1_wav, cube1_wav_size);
	sound_load_samples(&sounds[1], cube2_wav, cube2_wav_size);
	sound_load_samples(&sounds[2], cube3_wav, cube3_wav_size);
	sound_load_samples(&sounds[3], cube4_wav, cube4_wav_size);
}


void play_sound(u32 sound, s32 delay, s32 sl, s32 sr) {
	if (sound >= SOUND_NUM_SOUNDS)
		return;

	s32 free_voice = ASND_GetFirstUnusedVoice();
	if ((free_voice == SND_INVALID) || (free_voice < 1))
		return;

	if (ASND_Is_Paused())
		ASND_Pause(0);

	ASND_SetVoice(
			free_voice,
			sounds[sound].format,
			sounds[sound].pitch,
			delay, 
			sounds[sound].aligned_samples,
			sounds[sound].size,
			sl, 
			sr, 
			NULL);
}

void sound_text() {
	u16 bits = U16_LITTLE_TO_BIG(sounds[0].fmt->significant_bits);
	u16 num_channel = U16_LITTLE_TO_BIG(sounds[0].fmt->num_channel);
	DR_draw_text(50, 50, (GXColor){ 255, 255, 255, 255}, "L:%u, S:%u, F:%d, P:%d, B:%hu, C:%hu", sounds[0].loaded, sounds[0].size, sounds[0].format, sounds[0].pitch, bits, num_channel);
}
